package gbench.sandbox.tree;

import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.tree.LittleTree;

import static gbench.common.tree.LittleTree.Elem.*;
import static gbench.common.tree.LittleTree.Elem;
import static gbench.common.tree.LittleTree.Node;
import static gbench.common.tree.LittleTree.IRecord.*;

/**
 * Elem.cph的演示
 * 
 * @author gbench
 *
 */
public class JunitElemCphTree {

    @Test
    public void foo() {
        final var digits = "0123456789".split("");
        cph(digits, digits, digits).map(e -> e.data().collect(Collectors.toList())).forEach(System.out::println);
    }

    @Test
    @SuppressWarnings("unchecked")
    public void bar() {
        final var digits = "0123456789".split("");
        cph2(REC("a", digits, "b", digits).filter(p -> p.value().getClass().isArray())
                .reduce(p -> (Stream<String[]>) (Object)Stream.of(p._2()), Stream.of(), Stream::concat))
                        .forEach(System.out::println);
    }

    @Test
    public void qux() {
        final var mm = new HashMap<Elem<String>, Node<Elem<String>>>();// 临时工作的缓存map
        final var digits = "0123456789".split("");
        /**
         * 默认的Node对equals 以及 hashcode 的实现太过繁琐 因此这里给与 实现一个简单版本。 去除了很多通用条件 仅仅用于一个
         * 临时基于Elem<String>本地化的实现。用特化来提高 特化：子类化一个Node用于对实现Node的equals比较
         */
        final var quicknode_counter = new AtomicInteger(0);// quicknode_counter 的唯一标识申城器
        final class QuickNode extends Node<Elem<String>> {
            // constructor
            public QuickNode(final Elem<String> value) {
                super(value);
            }

            @Override
            public int hashCode() {
                return id;
            }

            @Override
            public boolean equals(final Object obj) {
                if (obj == null) return false;
                return obj.getClass() == QuickNode.class ? ((QuickNode) obj).id == (this.id) : false;
            }

            @Override
            public String getName() { // c重写名称获取函数
                return this.getValue()._1();
            }

            private final int id = quicknode_counter.getAndIncrement();// 本地节点标识
        }// QuickNode
        final var root = new QuickNode(Elem.cons2("root", null));
        
        final var quickelem_counter = new AtomicInteger(0);
        cph((e,p)->new Elem<String>(e,p){// 简化实现hashcode,加快 hashmap的检索,与  equals 比较
            @SuppressWarnings("unchecked") @Override
            public boolean equals(Object obj) { return this.getAttribute("id").equals(((Elem<String>)obj).getAttribute("id"));}
            
            @Override
            public int hashCode() { return id/1000;}
            
            private static final long serialVersionUID = 1L;
            private final int id = (Integer)setAttribute("id",quickelem_counter.getAndIncrement()).getAttribute("id");// 本地节点标识
        }, LittleTree.RPTA(5, digits))
        .forEach(e -> {
            final var parent_node = mm.get(e._2()); // 终端节点的父节点是否被添加
            if (parent_node == null) {// 如果没有被添加从终端叶节点一直到根节点都要被添加一边
                for (var p = e; p != null; p = p._2()) {// 从根节点 向 根节点遍历 :Elem是一个KVPair(this,parent)的二元组。
                    final var current = p;// 当前节点
                    final var parent = p._2(); // 父级节点
                    final var node_current = mm.computeIfAbsent(current, k -> new QuickNode(current)); // 转换成Node节点
                    final var node_parent = parent == null ? root
                            : mm.computeIfAbsent(parent, k -> new QuickNode(parent));// 获得当前节点的父节点的LittleTree.Node对象
                    if (node_parent.hasChild(node_current))
                        break;// 遭遇到共同的节点元素放弃该路径的继续添加
                    node_parent.addChild(node_current, false, false); // 添加子节点
                } // for
            } else {// 如果父节点已经被被添加过,则直接把新节点挂接到父节点之上。
                parent_node.addChild(new QuickNode(e), false, false);
            } // if e_parent
        });// forEach

        System.out.println("树形结构");
        root.forEach(e -> {
            System.out.println("\t".repeat(e.getLevel() - 1) + e.getPath());
        });
    }

    @Test
    public void qux2() {
        final var digits = "0123456789".split("");
        cph(LittleTree.RPTA(6, digits)).forEach(e -> {
            System.out.println(e);
        });
    }

}
